package netlink

import (
	"os"
	"syscall"
	"unsafe"
)

type NetlinkRequestData interface {
	Len() int
	Serialize() []byte
}

// linux/netlink.h
type NetlinkRequest struct {
	// 在 syscall/types_linux.go 中
    // type NlMsghdr C.struct_nlmsghdr
    // 可以看出 netlink 是 linux 上专用的，mac 不支持
	syscall.NlMsghdr
	Data []NetlinkRequestData
}

// 将 NetlinkRequest 中的全部内容序列化到一个大 buffer 中
func (req *NetlinkRequest) Serialize() []byte {
    // 在 syscall/types_linux.go 中
    // const SizeofNlMsghdr = C.sizeof_struct_nlmsghdr
	length := syscall.SizeofNlMsghdr
	dataBytes := make([][]byte, len(req.Data))
	for i, data := range req.Data {
        // 将每一个 NetlinkRequestData 序列化后保存
		dataBytes[i] = data.Serialize()
        // 累加总长度，包括 NlMsghdr 长度
        length = length + len(dataBytes[i])
	}
    // 填写正确的长度信息
	req.Len = uint32(length)
    // 创建保存所有数据的大 buffer
	b := make([]byte, length)
    // 取 header 内容
	hdr := (*(*[syscall.SizeofNlMsghdr]byte)(unsafe.Pointer(req)))[:]
	next := syscall.SizeofNlMsghdr
    // 拷贝 header 到大 buffer
	copy(b[0:next], hdr)
    // 逐个字节拷贝
	for _, data := range dataBytes {
		for _, dataByte := range data {
			b[next] = dataByte
			next = next + 1
		}
	}
	return b
}

// 按照 netlink 协议格式进行数据 append
// NetlinkRequestData 可能是数据，也可能是指令
func (req *NetlinkRequest) AddData(data NetlinkRequestData) {
	if data != nil {
		req.Data = append(req.Data, data)
	}
}

// 初始化一个 netlink 请求结构
func NewNetlinkRequest() *NetlinkRequest {
	return &NetlinkRequest{
		NlMsghdr: syscall.NlMsghdr{
			Len:   uint32(0),                  // Length of message (including header)
			Type:  uint16(syscall.NLMSG_DONE), // Message content
			Flags: uint16(0),                  // Additional flags
			Seq:   uint32(0),                  // Sequence number
			Pid:   uint32(os.Getpid()),        // Sending process port ID
		},
	}
}
